package roles

import (
	"context"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/sirupsen/logrus"

	"xagent/internal/actor"
	proto "xagent/proto/v1"
)

// ResearchTopic represents a research topic
type ResearchTopic struct {
	ID          string
	Title       string
	Description string
	Keywords    []string
	Status      string
	Priority    int
	CreatedAt   time.Time
	UpdatedAt   time.Time
	Findings    []ResearchFinding
	References  []Reference
	Metadata    map[string]interface{}
}

// ResearchFinding represents a research finding
type ResearchFinding struct {
	ID         string
	TopicID    string
	Content    string
	Confidence float64
	Evidences  []string
	CreatedAt  time.Time
	UpdatedAt  time.Time
	Tags       []string
}

// Reference represents a reference source
type Reference struct {
	ID        string
	Type      string
	Title     string
	Content   string
	URL       string
	Authors   []string
	CreatedAt time.Time
	UpdatedAt time.Time
}

// Researcher represents a researcher role
type Researcher struct {
	*actor.BaseActor
	topics     map[string]*ResearchTopic
	findings   map[string]*ResearchFinding
	references map[string]*Reference
	logger     *logrus.Logger
}

// NewResearcher creates a new researcher
func NewResearcher() *Researcher {
	baseActor := actor.NewBaseActor(
		"researcher",
		"Researcher",
		"Responsible for research and analysis",
	)

	r := &Researcher{
		BaseActor:  baseActor,
		topics:     make(map[string]*ResearchTopic),
		findings:   make(map[string]*ResearchFinding),
		references: make(map[string]*Reference),
		logger:     logrus.New(),
	}

	// Register actions
	r.AddAction(&AnalyzeTopicAction{r})
	r.AddAction(&ConductResearchAction{r})
	r.AddAction(&SynthesizeFindingsAction{r})

	return r
}

// Action implementations
type AnalyzeTopicAction struct {
	r *Researcher
}

func (a *AnalyzeTopicAction) Name() string {
	return "analyze_topic"
}

func (a *AnalyzeTopicAction) Prerequisites() []string {
	return nil
}

func (a *AnalyzeTopicAction) Run(ctx context.Context, input interface{}) (*proto.AgentMessage, error) {
	task, ok := input.(*proto.Task)
	if !ok {
		return nil, fmt.Errorf("invalid input type")
	}

	// Think about topic analysis
	thought, err := a.r.Think(ctx, fmt.Sprintf("Analyze research topic: %s", task.Description))
	if err != nil {
		return nil, err
	}

	// Create research topic
	topic := &ResearchTopic{
		ID:          uuid.NewString(),
		Title:       task.Name,
		Description: thought.Content,
		Keywords:    extractKeywords(thought.Content),
		Status:      "ACTIVE",
		Priority:    1,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
		Findings:    make([]ResearchFinding, 0),
		References:  make([]Reference, 0),
		Metadata:    make(map[string]interface{}),
	}

	a.r.topics[topic.ID] = topic

	return &proto.AgentMessage{
		Id:   uuid.NewString(),
		Type: proto.MessageType_MESSAGE_TYPE_RESPONSE,
		Content: &proto.AgentMessage_Message{
			Message: fmt.Sprintf("Research topic analyzed: %s\nKeywords: %v", topic.ID, topic.Keywords),
		},
		Metadata: map[string]string{
			"topic_id": topic.ID,
			"status":   topic.Status,
		},
	}, nil
}

type ConductResearchAction struct {
	r *Researcher
}

func (a *ConductResearchAction) Name() string {
	return "conduct_research"
}

func (a *ConductResearchAction) Prerequisites() []string {
	return []string{"analyze_topic"}
}

func (a *ConductResearchAction) Run(ctx context.Context, input interface{}) (*proto.AgentMessage, error) {
	task, ok := input.(*proto.Task)
	if !ok {
		return nil, fmt.Errorf("invalid input type")
	}

	// Get topic ID from metadata
	topicID, ok := task.Metadata["topic_id"]
	if !ok {
		return nil, fmt.Errorf("topic ID not provided")
	}

	topic, ok := a.r.topics[topicID]
	if !ok {
		return nil, fmt.Errorf("topic not found: %s", topicID)
	}

	// Think about research process
	thought, err := a.r.Think(ctx, fmt.Sprintf("Research topic: %s\nKeywords: %v", topic.Description, topic.Keywords))
	if err != nil {
		return nil, err
	}

	// Create findings from thought
	for i, finding := range thought.Plan {
		researchFinding := &ResearchFinding{
			ID:         uuid.NewString(),
			TopicID:    topic.ID,
			Content:    finding,
			Confidence: calculateConfidence(finding, thought.Reasoning),
			Evidences:  []string{thought.Reasoning},
			CreatedAt:  time.Now(),
			UpdatedAt:  time.Now(),
			Tags:       extractKeywords(finding),
		}

		topic.Findings = append(topic.Findings, *researchFinding)
		a.r.findings[researchFinding.ID] = researchFinding

		// Create reference
		reference := &Reference{
			ID:        uuid.NewString(),
			Type:      "research_finding",
			Title:     fmt.Sprintf("Finding %d", i+1),
			Content:   finding,
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
		}

		topic.References = append(topic.References, *reference)
		a.r.references[reference.ID] = reference
	}

	return &proto.AgentMessage{
		Id:   uuid.NewString(),
		Type: proto.MessageType_MESSAGE_TYPE_RESPONSE,
		Content: &proto.AgentMessage_Message{
			Message: fmt.Sprintf("Research conducted for topic %s: %d findings", topic.ID, len(topic.Findings)),
		},
		Metadata: map[string]string{
			"topic_id":         topic.ID,
			"findings_count":   fmt.Sprintf("%d", len(topic.Findings)),
			"references_count": fmt.Sprintf("%d", len(topic.References)),
		},
	}, nil
}

type SynthesizeFindingsAction struct {
	r *Researcher
}

func (a *SynthesizeFindingsAction) Name() string {
	return "synthesize_findings"
}

func (a *SynthesizeFindingsAction) Prerequisites() []string {
	return []string{"conduct_research"}
}

func (a *SynthesizeFindingsAction) Run(ctx context.Context, input interface{}) (*proto.AgentMessage, error) {
	task, ok := input.(*proto.Task)
	if !ok {
		return nil, fmt.Errorf("invalid input type")
	}

	// Get topic ID from metadata
	topicID, ok := task.Metadata["topic_id"]
	if !ok {
		return nil, fmt.Errorf("topic ID not provided")
	}

	topic, ok := a.r.topics[topicID]
	if !ok {
		return nil, fmt.Errorf("topic not found: %s", topicID)
	}

	// Think about synthesis
	thought, err := a.r.Think(ctx, fmt.Sprintf("Synthesize findings for topic: %s\nFindings: %v", topic.Description, topic.Findings))
	if err != nil {
		return nil, err
	}

	// Create synthesis reference
	synthesis := &Reference{
		ID:        uuid.NewString(),
		Type:      "synthesis",
		Title:     "Research Synthesis",
		Content:   thought.Content,
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	topic.References = append(topic.References, *synthesis)
	a.r.references[synthesis.ID] = synthesis

	// Update topic status
	topic.Status = "COMPLETED"
	topic.UpdatedAt = time.Now()

	return &proto.AgentMessage{
		Id:   uuid.NewString(),
		Type: proto.MessageType_MESSAGE_TYPE_RESPONSE,
		Content: &proto.AgentMessage_Message{
			Message: fmt.Sprintf("Research findings synthesized for topic %s:\n%s", topic.ID, thought.Content),
		},
		Metadata: map[string]string{
			"topic_id":     topic.ID,
			"synthesis_id": synthesis.ID,
			"status":       topic.Status,
		},
	}, nil
}

// Helper functions
func extractKeywords(text string) []string {
	// TODO: Implement proper keyword extraction
	return []string{"research", "analysis"}
}

func calculateConfidence(finding string, reasoning string) float64 {
	// TODO: Implement proper confidence calculation
	return 0.8
}

// Helper methods
func (r *Researcher) GetTopic(id string) *ResearchTopic {
	return r.topics[id]
}

func (r *Researcher) GetFinding(id string) *ResearchFinding {
	return r.findings[id]
}

func (r *Researcher) GetReference(id string) *Reference {
	return r.references[id]
}

func (r *Researcher) GetResearchMetrics() map[string]interface{} {
	metrics := make(map[string]interface{})

	totalTopics := len(r.topics)
	completedTopics := 0
	totalFindings := len(r.findings)
	totalReferences := len(r.references)

	var avgConfidence float64
	for _, finding := range r.findings {
		avgConfidence += finding.Confidence
	}
	if totalFindings > 0 {
		avgConfidence /= float64(totalFindings)
	}

	for _, topic := range r.topics {
		if topic.Status == "COMPLETED" {
			completedTopics++
		}
	}

	metrics["total_topics"] = totalTopics
	metrics["completed_topics"] = completedTopics
	metrics["completion_rate"] = float64(completedTopics) / float64(totalTopics)
	metrics["total_findings"] = totalFindings
	metrics["total_references"] = totalReferences
	metrics["average_confidence"] = avgConfidence

	return metrics
}
